package com.fanrui.code.chess;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import com.fanrui.code.player.Player;
import com.fanrui.code.player.PlayerManager;
import com.fanrui.proto.cmd.NotityPlayerLeaveMahjongDesk;
import com.fanrui.proto.cmd.ResJoinMahjongRoom.Result;
import com.fanrui.proto.cmd.ResPlayerLeaveMahjongDesk;
import com.google.protobuf.MessageLite;

/**
 * 棋牌游戏桌
 * @author Administrator
 *
 */
public abstract class AbstractChessGameTable<CARD extends IChessCard, GAMER extends IChessGamer<CARD>, GAMERULES extends IChessGameRules, GAMETYPE extends IChessGameType> implements IChessTable<CARD, GAMER, GAMERULES> {
	
	/**
	 * 牌桌ID
	 */
	private int ID;
	
	/**
	 * 游戏规则
	 */
	protected GAMERULES gameRules;
	
	/**
	 * 座位号对应的玩家
	 */
	protected ConcurrentMap<Integer, GAMER> gamers = new ConcurrentHashMap<Integer, GAMER>();
	
	/**
	 * 座位使用情况 二进制表示 1为被使用 0 未被使用
	 */
	protected int seatUseStatus = 0;
	
	/**
	 * 棋牌游戏中使用一副牌
	 */
	protected List<CARD> cards;
	
	/**
	 * 棋牌游戏中使用另一副牌
	 */
	protected List<CARD> backupCards;
	
	/**
	 * 庄家ID
	 */
	protected Long bankID = null;
	
	/**
	 * 已经进行的局数
	 */
	protected int roundNum = 0;
	
	/**
	 * 开始时间
	 */
	private final long creatTime;
	
	/**
	 * 房间类型 匹配或者私人
	 */
	protected GAMETYPE chessTableType;
	
	public AbstractChessGameTable(int ID, GAMERULES gameRules, GAMETYPE chessTableType) {
		this.ID = ID;
		this.gameRules = gameRules;
		this.creatTime = System.currentTimeMillis();
		this.chessTableType = chessTableType;
		this.cards = createCard();
		initialize();
	}
	
	/**
	 * 创建一副牌
	 * @return
	 */
	public abstract List<CARD> createCard();
	
	/**
	 * 进入房间的条件
	 * @param player 要进入房间的玩家
	 * @param isReconnect 是否重连
	 * @return
	 */
	public abstract Result checkEnterCondition(Player player, boolean isReconnect);
	
	public abstract GAMER findOrCreateGamer(Player player);
	
	/**
	 * 推送玩家进入 的消息 void
	 */
	public abstract void notifyPlayerEnter(GAMER gamer);
	
	/**
	 * 推送玩家准备的消息
	 */
	public abstract void havePlayerReady(GAMER gamer);
	
	/**
	 * 进入房间的方法
	 * @param isReconnect
	 * @param gamer
	 */
	public abstract void enter(boolean isReconnect, GAMER gamer);
	
	/**
	 * 是否正在进行游戏
	 * 
	 * @return boolean
	 */
	public abstract boolean isGaming();
	
	/**
	 * 玩家离开牌桌的条件判断
	 * 
	 * @param player
	 * @return CommonReply.Result
	 */
	public abstract ResPlayerLeaveMahjongDesk.Result checkExitCondition(Player player);
	
	/**
	 * 玩家是否还能存活
	 * @param gamer
	 * @return
	 */
	public abstract boolean checkPlayerSurvival(GAMER gamer);
	
	/**
	 * 推送玩家离开 的消息 void
	 */
	public abstract void notifyPlayerExit(GAMER gamer, NotityPlayerLeaveMahjongDesk.Result result);
	
	/**
	 * 获取发牌数量
	 * 
	 * @param playerID
	 * @return int
	 */
	public abstract int getDealNum(long playerID);
	
	/**
	 * 通知发牌
	 * @param gamer
	 */
	public abstract void notifyDealCard(GAMER gamer);
	
	/**
	 * 开始游戏
	 */
	public abstract void startGame();
	
	/**
	 * 扣创建房间的消耗
	 */
	public abstract boolean startDeductionCreateRoomCost();

	@Override
	public int getID() {
		return ID;
	}
	
	@Override
	public Result enter(Player player) {
		boolean isReconnect = gamers.containsKey(getSeatNoByPlayerID(player.getId()));
		Result result = checkEnterCondition(player, isReconnect);
		if (result != Result.Success) {
			return result;
		}
		synchronized (player) {
			GAMER gamer = findOrCreateGamer(player);
			if (gamer == null) {
				return Result.Failed;
			}
			// 设置第一个进入的玩家 为庄家
			if (getBankID() == null) {
				setBankID(player.getId());
			}
			addGamer(gamer);
			// 设置玩家信息
			player.setGamerId(gamer.getGamerID());
		}
		return Result.Success;
	}
	
	private void addGamer(GAMER gamer) {
		if(gamer == null)
			throw new NullPointerException("不能将空玩家 加入到牌桌中");
		synchronized (gamers) {
			boolean isReconnect = gamers.containsKey(getSeatNoByPlayerID(gamer.getPlayerID()));
			if (!isReconnect) {// 断线重连不做处理直接返回成功
				// 获取空闲的座位号
				final int idealSeatNo = getIdelSeatNO();
				GAMER oldGamer = gamers.putIfAbsent(idealSeatNo, gamer);
				if (oldGamer != null) {// 再次检查座位号
					throw new IllegalArgumentException("座位号错误");
				}
				useSeat(idealSeatNo);// 记录被使用的座位号
				notifyPlayerEnter(gamer);
			}
			enter(isReconnect, gamer);
		}
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public void playerReady(IChessGamer<?> gamer) {
		if(!gamer.isReady()) {
			gamer.setIsReady(true);
			havePlayerReady((GAMER)gamer);
			if (getReadyNum() == gameRules.maximumCapacity()) { // 准备开始游戏
				startGame();
			}
		}
	}
	
	/**  
	*  @return 已准备的玩家数量
	*/  
	protected int getReadyNum(){
		int totalReadyNum = 0;
		synchronized (gamers) {
			for (GAMER g : gamers.values()) {
				if (g.isReady()) {
					totalReadyNum++;
				}
			}
		}
		return totalReadyNum;
	}

	/**
	 * 使用座位号
	 * 
	 * @param seatIndex
	 *            void
	 */
	protected synchronized void useSeat(int seatIndex) {
		seatIndex = (int) Math.pow(2, seatIndex);
		if ((seatUseStatus & seatIndex) == seatIndex) {
			// 座位号已经被使用过了
			throw new IllegalArgumentException("座位号已经被使用过了");
		}
		seatUseStatus = seatUseStatus | seatIndex;
	}
	
	/**
	 * 不使用座位号
	 * 
	 * @param seatIndex
	 *            void
	 */
	protected synchronized void unusedSeat(int seatIndex) {
		seatIndex = (int) Math.pow(2, seatIndex);
		if ((seatUseStatus & seatIndex) != seatIndex) {
			// 座位号已经被使用过了
			throw new IllegalArgumentException("座位号已经被使用过了");
		}
		seatUseStatus = seatUseStatus ^ seatIndex;
	}
	
	/**
	 * 获取一个空闲的座位号
	 * 
	 * @return Integer
	 */
	protected synchronized Integer getIdelSeatNO() {
		// 根据牌桌的人数计算出人满的情况下的数值 例: 2个人的时候为11、3个人为111、4个人为1111
		int fullFlag = (1 << gameRules.maximumCapacity()) - 1; // 1111
		// 去除错误的座位号 只保留正确的座位号
		int currentSeatStatus = seatUseStatus & fullFlag;			  // 0000  &如果两个数都为1则为1，否则为0。^如果相同则为0，不相同则为1。
		// 当前空闲的座位号
		return Integer.numberOfTrailingZeros(Integer.lowestOneBit(fullFlag ^ currentSeatStatus));
	}

	/**
	 * 根据玩家ID获取在房间中的座位号
	 * @param playerId
	 * @return
	 */
	public int getSeatNoByPlayerID(Long playerId) {
		for (int seatNo : gamers.keySet()) {
			if (gamers.get(seatNo).getPlayerID() == playerId) {
				return seatNo;
			}
		}
		return -1;
	}

	@Override
	public void destory() {
		// 移除全部玩家
		Collection<Long> playerIDS = new ArrayList<Long>();
		for (GAMER gamer : gamers.values()) {
			playerIDS.add(gamer.getPlayerID());
		}
		for (long playerID : playerIDS) {
			exit(playerID, NotityPlayerLeaveMahjongDesk.Result.DISS);
		}
	}

	@Override
	public ResPlayerLeaveMahjongDesk.Result exit(long playerID, NotityPlayerLeaveMahjongDesk.Result exitCode) {
		Player player = PlayerManager.getInstance().findOrCreatePlayer(playerID);
		ResPlayerLeaveMahjongDesk.Result result = ResPlayerLeaveMahjongDesk.Result.Success;
		boolean jiesan = !isGaming();
		if (player != null) {
			result = checkExitCondition(player);
			if (result != ResPlayerLeaveMahjongDesk.Result.Success) {
				return result;
			}
		}
		Entry<Integer, GAMER> entry = findGamer(playerID);
		if (entry != null && jiesan) {
			removeGamer(entry);
			if(exitCode == NotityPlayerLeaveMahjongDesk.Result.HTTPGAMEOUT) {
				notifyPlayerExit(entry.getValue(), NotityPlayerLeaveMahjongDesk.Result.DISS);
			}else {
				notifyPlayerExit(entry.getValue(), exitCode);
			}
			if (player != null) {
				player.setGamerId(null);
			}
			entry.getValue().destory();
		}
		return result;
	}
	
	/**
	 * 查找玩家
	 * 
	 * @param playerID
	 * @return Entry<Integer,GAMER>
	 */
	public Entry<Integer, GAMER> findGamer(long playerID) {
		Iterator<Entry<Integer, GAMER>> iterator = gamers.entrySet().iterator();
		while (iterator.hasNext()) {
			Entry<Integer, GAMER> entry = iterator.next();
			if (entry.getValue().getPlayerID() == playerID) {
				return entry;
			}
		}
		return null;
	}
	
	/**
	 * 移除gamer
	 * 
	 * @param gamer
	 *            void
	 */
	protected void removeGamer(Entry<Integer, GAMER> gamer) {
		if (gamer != null) {
			synchronized (gamers) {
				if (gamers.containsKey(gamer.getKey()) && gamers.remove(gamer.getKey(), gamer.getValue())) {
					unusedSeat(gamer.getKey());
				}
			}
		}
	}

	/**
	 * 发牌 void
	 */
	protected void deal() {
		for (GAMER gamer : gamers.values()) {
			int dealNum = getDealNum(gamer.getPlayerID());
			if (dealNum >= cards.size()) {
				// log.error("发牌时，牌的数量不够");
				break;
			}
			List<CARD> result = new ArrayList<CARD>();
			for (int i = 0; i < dealNum; i++) {
				CARD card = cards.remove(0);
				result.add((CARD) card);
			}
			gamer.addCard(result);
			gamer.notifyStart();
		}
		for (GAMER gamer : gamers.values()) {
			notifyDealCard(gamer);
		}
	}
	
	public long getCreatTime() {
		return creatTime;
	}

	public GAMERULES getGameRules() {
		return gameRules;
	}

	public void setGameRules(GAMERULES gameRules) {
		this.gameRules = gameRules;
	}

	public ConcurrentMap<Integer, GAMER> getGamers() {
		return gamers;
	}

	public void setGamers(ConcurrentMap<Integer, GAMER> gamers) {
		this.gamers = gamers;
	}

	public int getSeatUseStatus() {
		return seatUseStatus;
	}

	public void setSeatUseStatus(int seatUseStatus) {
		this.seatUseStatus = seatUseStatus;
	}

	public List<CARD> getCards() {
		return cards;
	}

	public void setCards(List<CARD> cards) {
		this.cards = cards;
	}

	public Long getBankID() {
		return bankID;
	}

	public void setBankID(Long bankID) {
		this.bankID = bankID;
	}

	public int getRoundNum() {
		return roundNum;
	}

	public void setRoundNum(int roundNum) {
		this.roundNum = roundNum;
	}

	public GAMETYPE getChessTableType() {
		return chessTableType;
	}

	public void setChessTableType(GAMETYPE chessTableType) {
		this.chessTableType = chessTableType;
	}

	public void setID(int iD) {
		ID = iD;
	}
	
	protected void initialize() {
		backupCards = createCard();
	}
	
	public Collection<GAMER> getAllGamer() {
		return gamers.values();
	}

	@Override
	public void broadcast(MessageLite message) {
		synchronized (gamers) {
			for (GAMER gamer : gamers.values()) {
				gamer.send(message);
			}
		}
	}

	@Override
	public GAMER getGamerByPlayerID(long playerID) {
		synchronized (gamers) {
			for (GAMER gamer : gamers.values()) {
				if (gamer.getPlayerID() == playerID) {
					return gamer;
				}
			}
		}
		return null;
	}
	
	/**
	 * 获取下一位有效的玩家id
	 * 
	 * @param seatNo
	 * @return long
	 */
	public Long getNextExistSeatPlayerId(long playerId) {
		return getNextExistSeatPlayerId(getSeatNoByPlayerID(playerId));
	}
	
	/**
	 * 获取下一位有效的玩家id
	 * 
	 * @param seatNo
	 * @return long
	 */
	public Long getNextExistSeatPlayerId(int seatNo) {
		synchronized (gamers) {
			GAMER gamer = getGamerBySeatNO(getNextSeatNo(seatNo));
			while (gamer == null || !checkPlayerSurvival(gamer)) {
				seatNo = getNextSeatNo(seatNo);
				gamer = gamers.get(seatNo);
			}
			return gamer.getPlayerID();
		}
	}
	
	/**
	 * 获取下一个座位号
	 * 
	 * @param seatNO
	 * @return int
	 */
	public int getNextSeatNo(int seatNO) {
		if ((seatNO + 1) >= gameRules.maximumCapacity()) {
			return 0;
		} else {
			return (seatNO + 1);
		}
	}
	
	public int getGamerSeatNO(long playerID) {
		Iterator<Entry<Integer, GAMER>> iterator = gamers.entrySet().iterator();
		while (iterator.hasNext()) {
			Entry<Integer, GAMER> entry = iterator.next();
			if (entry.getValue().getPlayerID() == playerID) {
				return entry.getKey();
			}
		}
		return -1;
	}
	
	/**
	 * 房间是否已经满了
	 * 
	 */
	protected boolean isFull() {
		return getIdelSeatNO() == 32;
	}

	@Override
	public final void broadcastExclude(MessageLite message, long playerID) {
		synchronized (gamers) {
			for (GAMER gamer : gamers.values()) {
				if (gamer.getPlayerID() == playerID) {
					continue;
				}
				gamer.send(message);
			}
		}
	}
	
	@Override
	public GAMER getGamerBySeatNO(int seatNO) {
		return gamers.get(seatNO);
	}
	
	@Override
	public void dissolve() {
		// 移除全部玩家
		Collection<Long> playerIDS = new ArrayList<Long>();
		for (GAMER gamer : gamers.values()) {
			playerIDS.add(gamer.getPlayerID());
		}
		for (long playerID : playerIDS) {
			exit(playerID, NotityPlayerLeaveMahjongDesk.Result.DISS);
		}
	}
	
	@Override
	public long getCreateTime() {
		return creatTime;
	}
}
